#include <iostream>
#include <functional>
#include <atomic>
#include <mutex>
#include <thread>
#include <set>
#include <chrono>
#include <string>
#include <vector>
using namespace std;

struct TimerTask
{
	using task_t = function<void()>;
	task_t task;
	uint64_t delayed;
	uint64_t interval;

	TimerTask(task_t&& task, uint64_t delayed, uint64_t interval)
		:task(task)
		, delayed(delayed)
		, interval(interval)
	{ }

	bool operator<(const TimerTask& o) const
	{
		return delayed < o.delayed;
	}

};

class Timer
{
public:
	Timer():is_running(true), _timer(&Timer::run, this) {}

	void push(TimerTask::task_t&& task, uint64_t delayed, uint64_t interval)
	{
		std::unique_lock<std::mutex> ul(_mtx);
		auto now = std::chrono::high_resolution_clock::now();
		auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(
			now.time_since_epoch()
		).count();
		s.insert({ std::forward<TimerTask::task_t>(task), timestamp + delayed, interval });
	}

	void run()
	{
		while (is_running.load(std::memory_order_relaxed))
		{
			std::unique_lock<std::mutex> ul(_mtx);
			auto begin = s.begin();
			while (begin != s.end())
			{
				auto now = std::chrono::high_resolution_clock::now();
				auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(
					now.time_since_epoch()
				).count();
				auto cur = begin++;
				if (cur->delayed <= timestamp)
				{
					cur->task();
					if (cur->interval != 0)
					{
						TimerTask::task_t t = cur->task;
						ul.unlock();
						push(std::move(t), cur->interval, cur->interval);
						ul.lock();
					}
					s.erase(cur);
				}
			}
		}

	}

	void stop()
	{
		is_running.store(false);
	}


	~Timer()
	{
		stop();
		_timer.join();
	}
private:
	std::multiset<TimerTask> s;
	std::mutex _mtx;
	std::thread _timer;
	std::atomic<bool> is_running;
};

void Add(string x, string y)
{
	std::cout << x + y << std::endl;
}

int main()
{
	Timer timer;
	//std::thread t1(&Timer::run, &timer);

	timer.push([]() {std::cout << "11111" << std::endl; }, 2, 0);
	timer.push([]() {std::cout << "2222" << std::endl; }, 2, 2);
	timer.push([]() {std::cout << "2222" << std::endl; }, 2, 3);
	timer.push([]() {std::cout << "2222" << std::endl; }, 2, 4);
	timer.push([]() {std::cout << "2222" << std::endl; }, 2, 5);
	//timer.run();
	//for (int i = 0; i < 10; i++)
	//{
	//	timer.push(std::bind(&Add, to_string(i * 10), to_string(i * 11)), i, 1);
	//}

	while (true);
	//timer.stop();
	//t1.join();
	return 0;
}